package learn; /**
 * 单例模式
 * 一个类只能实例化一个对象，在类外不能实例化该对象（私有化构造方法）
 */

/**
 * 饿汉模式 因为静态变量实在类加载的时候就创建的，所以这个对象也是在类加载得时候就给创建到内存中了
 * 所以就给叫做饿汉模式 不过这样写的话缺点也很明显，过于勤快了，如果实例的对象在后面并没有
 * 使用，而且该对象很大，那么这样就白白浪费了很大的空间
 */
public class singleton {
    private static singleton instance = new singleton();

    //向外提供一个对象
    public static singleton getInstance() {
        return instance;//只是读，在多线程模式下并不会出现线程安全问题
    }

    //将构造方法私有化，外界就不能调用到构造方法，也就不能实例化对象
    private singleton() {}

}

/**
 * 懒汉模式 在要使用这个类的时候才实例这个对象
 * 在开始先不实例这个对象，在获取对象时才实例出这个对象 这样在需要时才创建更合理
 */
class singletonLazy {
    private static volatile singletonLazy instance = null;//这样的话也可能在俩个线程之间由于指令重排序的影响导致问题
    //在创建对象后解锁了，可能会执行一些初始化操作，这时有可能线程2直接掺进来执行他的代码，发现对象已经创建了然后直接得到这个对象
    //但是他这时得到的就是一个没有初始化的对象，这时就除了错误 加上volatile就可以解决指令重排序的问题
    //他会在第一个判断时插入一个内存屏蔽，阻止其他的instance插入到他的执行顺序中

    private static Object loker = new Object();
    public static singletonLazy getInstance() {
        if (instance == null) {//那么就在使用一次为空判断，这样在之后创建过了对象之后，就会避免上锁的问题
            //而if的执行的速度时一定比上锁顺序执行的快的
            synchronized (loker) {//那么就时用锁控制不同进程之间的执行顺序问题
                //可是这样写的话，每次不同线程之间获得对象都要进入阻塞状态，
                //而其实只有在第一次创建时会有作用，其他时候其实不需要阻塞的，这样就降低了性能
                if (instance == null) {
                    instance = new singletonLazy();//这样写在多线程的情况下很容易出问题
                    //但俩个线程同时进行判断且为空时，那么他们就都进入这个if里面，这样的话就创建了俩个对象
                    //虽然最终会有一个被覆盖掉，然后另一个被gc回收，可是若这个类特别大，那么这样一下就创建俩个对象
                    //就可能会造成内存不够啥的大问题
                }
            }
        }
        return instance;
    }

    private singletonLazy() {}
}
class text{
    public static void main(String[] args) {
        singleton s = singleton.getInstance();
        singleton s2 = singleton.getInstance();
        System.out.println(s == s2);
//        learn.singleton s1 = new learn.singleton();
    }
}
